<template>
  <div class="main-wrap">
    <div class="flow-context">
      <div ref="container" class="container-wrap"></div>
      <TeleportContainer />
    </div>

    <div class="flow-box">
      <div class="top-wrap">
        <div class="top-left-wrap">
          <el-row>
            <el-col :span="24" style="margin-bottom: 10px">
              <h3>流水线信息</h3>
            </el-col>
            <el-col :span="8">
              <span>任务名称：<span style="color: #3f9eff">{{ taskInfo.name }}</span></span>
            </el-col>
            <el-col :span="8">
              <span>命名空间：<span style="color: #3f9eff">{{ taskInfo.namespaceRel }}</span></span>
            </el-col>
            <el-col :span="8">
              <span>代码分支：<span style="color: #3f9eff">{{ getTaskBranch }}</span></span>
            </el-col>
          </el-row>
        </div>
        <div class="top-right-wrap">
          <el-button type="success" :icon="VideoPlay" :loading="taskStatus" @click="handleRunTask">运行</el-button>
          <el-button type="danger" :icon="VideoPause" @click="handleStopTask">停止</el-button>
          <el-tooltip
              effect="dark"
              content="查看代码分支"
              placement="bottom"
          >
            <el-button type="primary" :icon="Share" @click="openBranch">{{ getTaskBranch }}</el-button>
          </el-tooltip>
          <el-button type="info" :icon="Histogram" @click="handleRunLog">记录</el-button>
          <el-button :icon="Edit" @click="handleToEdit">编辑</el-button>
        </div>
      </div>
      <div class="log-box">
        <div class="log-wrap" id="log-container">
          <p v-for="(item,index) in msgList" :key="index" :style="{color: getMsgColor(item.type)}">
            <span style="color: #A8ABB2;">{{ item.time }}</span> {{ item.msg }}
          </p>
        </div>
      </div>
    </div>

    <el-drawer v-model="drawerFlag" direction="rtl" size="700" :with-header="false">
      <el-table :data="hisData?.records" height="calc(100vh - 100px)" v-loading="hisLoading">
        <el-table-column width="90" label="构建序号" fixed>
          <template #default="scope">
            <el-tooltip effect="light" content="查看日志" placement="top-start">
              <span style="color: #409EFF; cursor: pointer;" @click="openHistoryLog(scope.row)">#{{ scope.row.batchId }}</span>
            </el-tooltip>
          </template>
        </el-table-column>
        <el-table-column width="70" label="状态">
          <template #default="scope">
            <el-tag v-if="scope.row.status"
                    effect="dark"
                    :type="getTaskStatusTagType(scope.row.status as TaskStatus)"
                    round
            >
              {{ taskStatusTextMap[scope.row.status as TaskStatus] }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column width="100" label="分支">
          <template #default="scope">
            <el-link :underline="false" style="color: #409EFF;" :icon="Share">{{ scope.row.gitBranch }}</el-link>
          </template>
        </el-table-column>
        <el-table-column property="gitMsg" label="提交信息" />
        <el-table-column width="180" property="createTime" label="开始时间" />
      </el-table>
      <el-pagination
          v-model:current-page="currentPage"
          :page-size="pageSize"
          small
          layout="total, prev, pager, next"
          :total="hisData ? hisData.totalRow : 0"
          @current-change="handleCurrentChange"
      />
    </el-drawer>

    <el-dialog
        v-model="logShow"
        :title="logTitle"
        fullscreen
        :before-close="handleLogClose"
    >
      <div v-loading="logLoading" class="his-log">
        <p v-for="(item,index) in logList" :key="index">{{ item }}</p>
      </div>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="handleLogClose">关闭</el-button>
        </span>
      </template>
    </el-dialog>

    <el-dialog
        v-model="branchFlag"
        title="切换分支"
        width="30%"
        :before-close="handleBranchClose"
    >
      <div v-loading="branchLoading">
        <el-row>
          <el-col :span="24">
            <span>当前分支：<span style="color: #409EFF;">{{ getTaskBranch }}</span></span>
          </el-col>
          <el-col :span="24">
            <el-select style="margin-top: 10px;" v-model="selectBranch" placeholder="选择分支" size="small">
              <el-option
                  v-for="(item,index) in branchList"
                  :key="index"
                  :label="item"
                  :value="item"
              />
            </el-select>
          </el-col>
        </el-row>
      </div>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="handleBranchClose">取消</el-button>
          <el-button type="primary" @click="handleBranch">确定</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>

<script lang="ts" setup>
import {ref, onMounted, computed, nextTick, onBeforeUnmount} from 'vue'
import {
  getHisLog,
  getTaskHisList,
  getTaskInfo,
  runTask,
  stopTask,
  TaskHisData,
  type TaskInfo,
  getBranchList,
  updateTaskData
} from "@/api/TaskApi.ts"
import {ElLoading, ElMessage} from "element-plus";
import {VideoPlay, VideoPause, Histogram, Share, Edit} from "@element-plus/icons-vue";
import { useRoute } from 'vue-router'
import {NodeType} from "@/enum/NodeType.ts";
import {GitData, MsgData, getMsgColor, NodeStatusData, FlowStatusData} from "@/data/TaskData.ts";
import { initViewGraph, handleRegisterNode } from "@/utils/AntvUtil.ts"
import { getTeleport } from '@antv/x6-vue-shape'
import router from "@/router";
import { initSSE, closeSSE } from '@/utils/SSEUtil.ts'
import { MsgType } from '@/enum/MsgType'
import {Graph} from "@antv/x6";
import {TaskStatus, taskStatusTagTypeMap, taskStatusTextMap} from "@/enum/TaskStatus.ts";
import {Page} from "@/data/PageData.ts";
import {NodeStatus} from "@/enum/NodeStatus.ts";

const taskInfo = ref<TaskInfo>({} as TaskInfo)
const route = useRoute()
const taskId = route.query.id as string
const taskStatus = ref<boolean>(false)
const container = ref<HTMLElement>({} as HTMLElement)
const TeleportContainer = getTeleport()
const msgList = ref<Array<MsgData>>([])
let graph:Graph
const drawerFlag = ref<boolean>(false)
const hisData = ref<Page<TaskHisData>>()
const hisLoading = ref<boolean>(false)
const pageSize = ref<number>(10)
const currentPage = ref<number>(1)
const logShow = ref<boolean>(false)
const logTitle = ref<string>('')
const logLoading = ref<boolean>(false)
const logList = ref<Array<string>>([])
const branchFlag = ref<boolean>(false)
const branchLoading = ref<boolean>(false)
const branchList = ref<Array<string>>([])
const selectBranch = ref<string>('')

onMounted(() => {
  initWorkSpace()
})

const initWorkSpace = async () => {
  await initTaskWork(taskId)
  handleRegisterNode()
  graph = initViewGraph(container.value, taskInfo.value)
  handleSSE(taskId)
}

const getTaskBranch = computed(() => {
  if(!taskInfo.value.taskDataList){
    return 'master'
  }
  const gitData = taskInfo.value.taskDataList.find(i => i.type === NodeType.GIT);
  if(!gitData || !gitData.data){
    return 'master'
  }
  const data = gitData.data as GitData;
  return data.branch ? data.branch : data.defaultBranch
})

const initTaskWork = async (taskId:string) => {
  if(!taskId){
    return
  }
  const loading = getLoading('流程数据加载中...')
  const res = await getTaskInfo(taskId, false)
  loading.close()
  if(res.data){
    taskInfo.value = res.data
  }
}

const getLoading = (msg:string):any  =>{
  return ElLoading.service({
    lock: true,
    text: msg,
    background: 'rgba(0, 0, 0, 0.7)',
  })
}

const handleRunTask = async () => {
  if(taskStatus.value){
    return
  }
  await initWorkSpace()
  await runTask(taskId)
  taskStatus.value = true
  msgList.value = []
}

const handleStopTask = async () => {
  await stopTask(taskId)
}

const openBranch = async () => {
  selectBranch.value = getTaskBranch.value
  branchFlag.value = true
  branchLoading.value = true
  const rs = await getBranchList(taskId)
  branchLoading.value = false
  branchList.value = rs.data
}

const handleBranchClose = () => {
  branchFlag.value = false
}

const handleBranch = async () => {
  const gitData = taskInfo.value.taskDataList.find(i => i.type === NodeType.GIT)
  if(!gitData || !gitData.data){
    ElMessage.error('数据错误')
    return
  }
  const data = gitData.data as GitData;
  data.branch = selectBranch.value
  taskInfo.value.taskDataList.forEach(i => {
    i.status = NodeStatus.INIT
  })

  const loading = getLoading('正在保存数据...')
  await updateTaskData(taskInfo.value)
  loading.close()
  ElMessage.success('保存成功')
  branchFlag.value = false
}

const handleToEdit = () => {
  router.push({ name: 'editFlow', query:{ id: taskId } })
}

const handleSSE = (taskId:string) => {
  initSSE(taskId, (msgData:MsgData) => {
    if(MsgType.NODE == msgData.type){
        nodeStatusHandle(msgData)
        return
    }

    if(MsgType.FLOW == msgData.type){
        flowStatusHandle(msgData)
        return
    }

    if(MsgType.ERROR == msgData.type){
        errorLogHandle()
    }

    setTimeout(() => {
        if(msgList.value.length > 1000){
            msgList.value = msgList.value.splice(300, msgList.value.length)
        }
        msgList.value.push(msgData)
        scrollToBottom()
    }, 500)
  })
}

const nodeStatusHandle = (msgData:MsgData) => {
  const nodeStatusData = msgData.data as NodeStatusData
  graph.getNodes().forEach(i => {
    if(i.id == nodeStatusData.id){
      i.setData({
        status: nodeStatusData.status
      })
    }
  })
}

const flowStatusHandle = (msgData:MsgData) => {
  const flowStatusData = msgData.data as FlowStatusData
  taskStatus.value = flowStatusData.status
  if(taskStatus.value){
    msgList.value = []
  }
}

const scrollToBottom = () => {
  const container = document.getElementById('log-container')
  if(!container){
    return
  }
  nextTick(() => {
    container.scrollTop = container.scrollHeight
  })
}

const errorLogHandle = () => {
  taskStatus.value = false
}

onBeforeUnmount( () => {
  closeSSE(taskId)
})

window.onbeforeunload = function () {
  closeSSE(taskId)
}

const getTaskStatusTagType = (status:TaskStatus): any => {
  return taskStatusTagTypeMap[status]
}

const handleRunLog = () => {
  drawerFlag.value = true
  currentPage.value = 1
  queryTaskHis()
}

const queryTaskHis = async () => {
  hisLoading.value = true
  const rs = await getTaskHisList({taskId: taskId, pageSize: pageSize.value, pageNum: currentPage.value})
  hisData.value = rs.data
  hisLoading.value = false
}

const handleCurrentChange = (val: number) => {
  currentPage.value = val
  queryTaskHis()
}

const handleLogClose = () => {
  logShow.value = false
  logList.value = []
}

const openHistoryLog = async (data:TaskHisData) => {
  logShow.value = true
  logLoading.value = true
  logTitle.value = `日志文件名称：${data.logFile}`
  const rs = await getHisLog(data.taskId, data.batchId)
  logLoading.value = false
  logList.value = rs.data
}
</script>

<style lang="less" scoped>
.main-wrap{
  padding: 10px;

  .flow-context{
    height: 120px;
    width: 100%;
    display: flex;

    .container-wrap{
      flex: 1;
      border-radius: 5px;
    }
  }

  .flow-box{
    padding-top: 10px;
    width: 100%;

    .top-wrap{
      height: 85px;
      width: 100%;
      display: flex;
      border-radius: 10px;

      .top-left-wrap{
        width: 750px;
        height: 100%;
        padding: 10px;
      }

      .top-right-wrap{
        flex-basis: auto;
        flex-grow: 1;
        height: 100%;
        display: flex;
        justify-content: flex-end;
        align-items: self-end;
        padding: 10px;
      }
    }

    .log-box{
      background-color: #FFFFFF;

      .log-wrap{
        height: calc(100vh - 283px);
        background-color: #000000;
        padding: 10px;
        border-radius: 5px;
        color: #FFFFFF;
        overflow-wrap: break-word;
        overflow-y: scroll;

        p{
          margin: 0;
        }
      }
    }
  }

  .his-log{
    height: calc(100vh - 120px);
    background-color: #000000;
    color: #FFFFFF;
    padding: 10px;
    border-radius: 5px;
    overflow-y: scroll;
  }
}
</style>